package com.example.sharding.dict;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.api.R;
import com.example.sharding.enerty.TAnnotationDataItem;
import com.example.sharding.service.TAnnotationDataItemService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @program: test_demo
 * @description: dict注解对应实现类
 * @author: yanghua
 * @create: 2021-11-02 14:47
 **/
@Aspect
@Component
@Slf4j
public class DictAspect {
    //表对应字段加上_dictText即可显示出文本
    private static String DICT_TEXT_SUFFIX = "Name";

    @Resource
    TAnnotationDataItemService tannotationDataItemService;


    //定义切点Pointcut拦截所有对服务器的请求
    //@Pointcut("execution( * com.example.test_demo.shardingSphere.controller.*.*(..))")
    @Pointcut("execution( * com.example.sharding.service.*.*(..))")
    //@Pointcut("@annotation(com.example.test_demo.dict.Dict)")//没想到这种切入方式不生效
    public void excudeService() {

    }


    /**
     * 这是触发DictionariesService的时候会执行的，在环绕通知中目标对象方法被调用后的结果进行再处理
     *
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("excudeService()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
        //这是定义开始事件
        long time1 = System.currentTimeMillis();
        //这是方法并获取返回结果
        Object result = pjp.proceed();
        //这是获取到结束时间
        long time2 = System.currentTimeMillis();
        log.info("获取JSON数据耗时：" + (time2 - time1) + "ms");
        //解析开始时间
        long start = System.currentTimeMillis();
        //开始解析（翻译字段内部的值凡是打了@Dict这玩意的都会被翻译）
        this.parseDictText(result);
        //解析结束时间
        long end = System.currentTimeMillis();
        log.info("解析注入JSON数据耗时：" + (end - start) + "ms");
        return result;
    }


    private void parseDictText(Object result) {
        List list = new ArrayList<>();
        log.info("响应体的类型:{}", result.getClass());

        if (result instanceof R) {//单一实体包装一个R

            if (((R) result).getData()==null){
                log.info("响应体类型不再解析范围内");
                return;
            }

            list.add(((R) result).getData());
        }

        if (result instanceof IPage) {//返回类型最外层的包装壳是IPage
            list = ((IPage) result).getRecords();
        }
        if (result instanceof Iterable) {//返回类型最外层的包装壳是list
            list = (List) result;
        }

        if (CollectionUtils.isEmpty(list)){
            log.info("响应体类型不再解析范围内");
            return;
        }

        //查询数据字典获取实体类中字典注解的值
        Object record1 = list.get(0);
        //解决继承实体字段无法翻译问题
        Map<String, Map<String, List<TAnnotationDataItem>>> map = new HashMap<>();
        for (Field field : ObjConvertUtils.getAllFields(record1)) {
            //解决继承实体字段无法翻译问题
            if (field.getAnnotation(Dict.class) != null) {//如果该属性上面有@Dict注解，则进行翻译
                String datasource = field.getAnnotation(Dict.class).dictDataSource();//拿到注解的dictDataSource属性的值
                Map<String, List<TAnnotationDataItem>> stringListMap = this.translateDictValueBanch(datasource);
                if (stringListMap!=null){
                    map.put(datasource,stringListMap);
                }
            }
        }

        //循环查找出来的数据
        for (Object record : list) {

            //解决继承实体字段无法翻译问题
            for (Field field : ObjConvertUtils.getAllFields(record)) {
                //获取类的成员变量名
                String fieldName = field.getName();
                //解决继承实体字段无法翻译问题
                if (field.getAnnotation(Dict.class) != null) {//如果该属性上面有@Dict注解，则进行翻译
                    // 获得set方法
                    Method setMethod = null;
                    // 获得get方法
                    Method getMethod = null;
                    //获取类的成员变量类型
                    //String dataType = null;
                    try {
                        // 获得属性描述器 如属性在父类：object.getClass().getSuperclass()
                        PropertyDescriptor propertyDescriptor = new PropertyDescriptor(fieldName, record.getClass());

                        String dictTextName = field.getAnnotation(Dict.class).dictText();//拿到注解的dictText属性的值,这个属性值用来存储字典的val值
                        if (!(dictTextName==null||"".equals(dictTextName))){//有这个属性就赋值
                            PropertyDescriptor propertyDescriptor2 = new PropertyDescriptor(dictTextName, record.getClass());

                            setMethod = propertyDescriptor2.getWriteMethod();
                        }else {//没有这个属性就赋值,就把原来存储code的字段赋值为val

                            setMethod = propertyDescriptor.getWriteMethod();
                        }

                        getMethod = propertyDescriptor.getReadMethod();

                        //dataType = propertyDescriptor.getPropertyType().getName();

                    } catch (IntrospectionException e) {
                        e.printStackTrace();
                    }

                    String datasource = field.getAnnotation(Dict.class).dictDataSource();//拿到注解的dictDataSource属性的值
                    //获取当前等待翻译成员变量的值
                    String code = null;
                    try {
                        code = (String)getMethod.invoke(record);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }
                    //翻译字典值对应的val值
                    Map<String, List<TAnnotationDataItem>> stringListMap = map.get(datasource);
                    StringBuffer textValue = new StringBuffer();
                    if (code.contains(",")){
                        //分割key值
                        String[] keys = code.split(",");
                        //循环keys中的所有值
                        for (String k : keys) {
                            String tmpValue = null;
                            log.debug("字典key：" + k);
                            if (k.trim().length() == 0) {
                                continue;//跳过循环
                            }

                            List<TAnnotationDataItem> tAnnotationDataItems = stringListMap.get(k.trim());
                            tmpValue = tAnnotationDataItems.get(0).getVal();

                            if (tmpValue != null) {
                                if (!"".equals(textValue.toString())) {
                                    textValue.append(",");
                                }
                                textValue.append(tmpValue);
                            }
                        }
                    }else {
                        List<TAnnotationDataItem> tAnnotationDataItems = stringListMap.get(code);
                        textValue.append(tAnnotationDataItems.get(0).getVal());
                    }

                    // 调用指定对象set方法
                    try {
                        setMethod.invoke(record, textValue.toString());
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }


                }

            }

        }

    }


    /**
     * 翻译字典文本(只通过类型参数获取与之对应的所有key和val)
     *
     * @param datasource
     * @return
     */
    private Map<String, List<TAnnotationDataItem>> translateDictValueBanch(String datasource) {
        //如果datasource为空直接返回就好了
        if (ObjConvertUtils.isEmpty(datasource)) {
            return null;
        }
        Map<String, List<TAnnotationDataItem>> stringListMap = tannotationDataItemService.selectByDatasourceKeyBanch(datasource);

        //返回翻译的值
        return stringListMap;
    }
}
